Nuevamente, vamos a leer primero unos datos...
In [ ]:
# primero hacemos los imports de turno
import os
import datetime as dt
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display
np.random.seed(19760812)
%matplotlib inline
In [ ]:
# Leemos los datos del fichero 'mast.txt'
ipath = os.path.join('Datos', 'mast.txt')
def dateparse(date, time):
YY = 2000 + int(date[:2])
MM = int(date[2:4])
DD = int(date[4:])
hh = int(time[:2])
mm = int(time[2:])
return dt.datetime(YY, MM, DD, hh, mm, 0)
cols = ['Date', 'time', 'wspd', 'wspd_max', 'wdir',
'x1', 'x2', 'x3', 'x4', 'x5',
'wspd_std']
wind = pd.read_csv(ipath, sep = "\s*", names = cols,
parse_dates = [[0, 1]], index_col = 0,
date_parser = dateparse)
Podemos acceder a los datos usando indexación como haríamos con un numpy array o como se hace normalmente en Python:
In [ ]:
wind[0:10]
In [ ]:
wind['2013-09-04 00:00:00':'2013-09-04 01:30:00']
En este segundo ejemplo la indexación se hace mediante strings que son la representación de los índices (etiquetas de las posiciones). Otra cosa a resaltar es que en el slice el último elemento SÍ se incluye.
En ejemplos anteriores, también hemos visto que podíamos seleccionar columnas usando el nombre de la columna:
In [ ]:
wind['wspd'].head(3)
Depende como sean los nombres de las columnas, se puede acceder a la columna usando la notación 'de punto' (dot notation) pero como no siempre funciona y es propenso a errores os recomiendo no usar esta forma:
In [ ]:
# Esto es equivalente a lo de la celda anterior
wind.wspd.head(3)
In [ ]:
# Un ejemplo de potencial error
df1 = pd.DataFrame(np.random.randn(5,2), columns = [1, 2])
df1
In [ ]:
# Esto dará error
df1.1
In [ ]:
# Para que no dé error
df1[1]
También se puede utilizar Fancy indexing con series, como si estuviéramos indexando con una lista o con un array de booleanos:
In [ ]:
# Creamos una serie
wspd = wind['wspd']
# Accedemos a los elementos situados en las posiciones 0, 100 y 1000
print(wspd[[0, 100, 1000]])
print('\n' * 3)
# Usamos los índices en las posiciones 0, 100 y 1000
idx = wspd[[0, 100, 1000]].index
print(idx)
print('\n' * 3)
# Accedemos a los mismos que inicialmente pero usando los índices en lugar de
# las posiciones de los valores
print(wspd[idx])
Con DataFrame
s el fancy indexing puede resultar ambiguo y nos dará error de indexación.
In [ ]:
# Intentadlo...
Como con numpy, también podemos acceder a los valores mediante condiciones booleanas:
In [ ]:
idx = wind['wspd'] > 35
wind[idx]
Podemos encadenar diferentes condiciones. Por ejemplo, vamos a refinar el resultado anterior:
In [ ]:
idx = (wind['wspd'] > 35) & (wind['wdir'] > 225)
wind[idx]
Usando condiciones booleanas puede resultar menos legible. Desde la versión de pandas 0.13 podemos usar el método query
para hacerlo un poco más legible.
In [ ]:
# Para que sea más eficiente hay que tener instalado 'numexpr'
# Que es el engine por defecto. Si no lo tenemos instalado y
# no indicamos el engine ('python') nos dará ImportError
wind.query('wspd > 35 and wdir > 225', engine = 'python')
Mediante estas formas de selección pueden llegar a existir ciertas ambigüedades. Vamos a hacer un pequeño inciso para después volver a formas de selección más avanzadas.
In [ ]:
s1 = pd.Series(np.arange(0,10), index = np.arange(0,10))
s2 = pd.Series(np.arange(10,20), index = np.arange(5,15))
print(s1)
print(s2)
Si ahora realizamos una operación entre ambas Series
, donde es posible realizar la operación se realiza y en los índices donde no es posible se representan pero no desaparecen ni nos proporciona un error:
In [ ]:
s1 + s2
Una de las características básicas de pandas es el etiquetado de filas y de columnas, ello provoca que la indexación sea más compleja que con numpy. Hemos de distinguir entre:
La indexación en Series
es más simple puesto que las etiquetas siempre se refieren a los índices ya que la columna es única. Como hemos ido viendo anteriormente de forma un poco difusa, para un DataFrame
, la indexación básica selecciona las columnas.
Para elegir una sola columna, como ya hemos visto anteriormente:
In [ ]:
wind['wspd_std']
O, podemos elegir varias columnas:
In [ ]:
wind[['wspd', 'wspd_std']]
Pero mediante una 'rebanada' (slicing) accedemos a los índices:
In [ ]:
wind['2015/01/01 00:00':'2015/01/01 02:00']
Por lo que esto daría error
In [ ]:
wind['wspd':'wdir']
In [ ]:
wind[['wspd':'wdir']]
Vaya lío, ¿no?
Para realizar una indexación más avanzada y menos ambigua disponemos de una serie de métodos:
loc
: está pensado para basar nuestra indexación en las etiquetas (aunque puede aceptar arrays booleanos).
iloc
: esta opción se basa en usar las posiciones mediante enteros (como si fuera un numpy array).
ix
: soporta combinar las dos anteriores.
Estos métodos también están disponibles en las Series
pero no serían necesarios ya que la indexación en las Series
no debe resultar ambigua.
Veamos como funcionan estos métodos en un DataFrame
...
Seleccionamos los primeros tres elementos de las dos primeras columnas ('wspd', 'wspd_max'):
In [ ]:
wind.loc['2013-09-04 00:00:00':'2013-09-04 00:20:00', 'wspd':'wspd_max']
In [ ]:
wind.iloc[0:3, 0:2] # similar a indexación con numpy arrays wind.values[0:3, 0:2]
In [ ]:
wind.ix[0:3, 'wspd':'wspd_max']
Una cuarta forma que no hemos visto hasta ahora de forma deliberada:
In [ ]:
wind[0:3][['wspd', 'wspd_max']]
In [ ]:
wind[['wspd', 'wspd_max']][0:3]
Devolved todos los datos de enero de 2014
Calculad la velocidad media de febrero de 2014
Usad el método query
para obtener todas las velocidades que provienen del norte (en un rango de $\pm$ 10 º considerando el norte orientado a 0º) y cuya velocidad sea superior a 10 m/s
Lo mismo que el punto anterior pero usando la notación booleana típica de numpy
Todo lo anterior lo podéis hacer usando loc
, iloc
y/o ix
. Practicad con las tres posibilidades.
In [ ]:
In [ ]:
wind.between_time('00:00', '00:30').head(20)
In [ ]:
# También funciona en series:
wind['wspd'].between_time('00:00', '00:30').head(20)